/*******************************************************************/
/*    Copyright (c) 1999-2020 by Spatial Corp.                     */
/*    All rights reserved.                                         */
/*    Protected by U.S. Patents 5,257,205; 5,351,196; 6,369,815;   */
/*                              5,982,378; 6,462,738; 6,941,251    */
/*    Protected by European Patents 0503642; 69220263.3            */
/*    Protected by Hong Kong Patent 1008101A                       */
/*******************************************************************/

//----------------------------- IopAcisPMI.cpp -------------------------------

#include <iostream>
#include <vector>

//ACIS
#include "lists.hxx"
#include "kernapi.hxx" 

//IOP
#include "SPAIopAcis.h"
#include "SPAIopPolicyOptions.h"
#include "SPAIopPMIUtils.h"
#include "SPAIopArgParser.h"
#include "SPAIopSampleUtilities.h"
#include "SPAIopPrintUtilities.h"
#include "SPAIopAcisSampleUtilities.h"
#include "SPAIopSampleResultUtils.h"
#include "SPAIopPerformanceStatistics.h"
//-----------------------------------------------------------------------------------------

using namespace SPAIopSampleLogging;
using namespace SPAIopAcisProductStructureUtils;
using namespace SPAIopPrintUtilities;
using namespace SPAIopAcisPrintUtils;
using namespace SPAIopPMIUtils;
using namespace SPAIopAcisPMIUtils;

//-----------------------------------------------------------------------------------------

static std::vector<ENTITY_LIST*> toBeReleased;

//-----------------------------------------------------------------------------------------

// License file
#include "spatial_license.h"
#include "spa_unlock_result.hxx"

int ImportPMIData(const wchar_t* ipSourceFilePath, const SPAIopPolicy& iPolicy);
int ImportPartPMI(SPAIopPartDocument& iIopPartDocument, const unsigned int& iRefId, const SPAIopPolicy& iPolicy);
int ImportParts(ImportedPartReferenceList iImportedPartList, const SPAIopPolicy& iPolicy);
void ImportAssemblyLevelPMI(SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy);
void ReleaseAcisEntities();

//-----------------------------------------------------------------------------------------

void PrintHelp()
{
	PrintMessage(L"Usage: IopAcisPMI -i <full-input-file-path> [-o <full-output-dump-file-path>] [-p <full-policy-file-path>] [-log <full-log-file-path>] [-h] \n");
	PrintMessage(L"\t-i  : The absolute path to the input file. \n");
	PrintMessage(L"\t-o  : The absolute path to the output dump file(.txt). \n");
	PrintMessage(L"\t-p  : The absolute path to the policy file used to control optional behavior during operations. \n");
	PrintMessage(L"\t-log: The absolute path to the log file. \n");
	PrintMessage(L"\t-h  : This help.\n");
}

//-----------------------------------------------------------------------------------------

int ImportPMIData(const wchar_t* ipSourceFilePath, const SPAIopPolicy& iPolicy)
{
	int returnValue = SPAIopSampleResult_Import_Ok;

	// Import Input Product Structure 
	SPAIopInputProductStructure inputPS;
	bool gotPS = ImportProductStructure(ipSourceFilePath, iPolicy, inputPS);

	if (true == gotPS)
	{
		ImportedPartReferenceList importedPartList;
		SPAIopPSImportResult inputPSResult;
		ImportPartReference(inputPS, inputPSResult, importedPartList);

		returnValue = ImportParts(importedPartList, iPolicy);

		importedPartList.clear();

		// Import Assembly level PMI Data
		std::wstring sourceFilePath(ipSourceFilePath);
		SPAIopWString wSourceFilePath(sourceFilePath.c_str());
		SPAIopDocument srcDoc(wSourceFilePath);
		SPAIopDocumentInformation docInfo = srcDoc.GetInformation();
		SPAIopDocumentType docType = SPAIopDocumentType_Unknown;
		docInfo.GetType(docType);
		if (SPAIopDocumentType_Assembly == docType || SPAIopDocumentType_PartAndAssembly == docType)
		{
			ImportAssemblyLevelPMI(inputPS, iPolicy);
		}

		ReleaseAcisEntities();
		SPAIopPMIUtils::ReleaseMappingData();
	}
	else
		returnValue = SPAIopSampleResult_Import_PS_Fail;

	return returnValue;
}

//-----------------------------------------------------------------------------------------

int ImportParts(ImportedPartReferenceList iImportedPartList, const SPAIopPolicy& iPolicy)
{
	int result = SPAIopSampleResult_Import_Ok;
	for (ImportedPartReferenceList::iterator bit = iImportedPartList.begin(); bit != iImportedPartList.end(); ++bit)
	{
		SPAIopInputPSReference* pReference = *bit;
		if (NULL != pReference)
		{
			PrintPSReferenceInfo(*pReference);
			SPAIopPartDocument partReferneceDoc = pReference->GetDocument();

			unsigned int uniqueID = pReference->GetUniqueID();
			int res = ImportPartPMI(partReferneceDoc, uniqueID, iPolicy);
			if (0 != res)
				result = res;

			if (SPAIopSampleResult_Import_Part_Fail == res)
			{
				unsigned int uniqueID = pReference->GetUniqueID();
				SPAIopWString storageNamePath = pReference->GetStorageName();
				SPAIopWString referenceName = pReference->GetReferenceName();

				SPAIopWString storageName;
				GetNameWithExtension(storageNamePath, storageName);

				const wchar_t* pStorageName = NULL;
				storageName.ToWChar(pStorageName);

				const wchar_t* pReferenceName = NULL;
				referenceName.ToWChar(pReferenceName);

				PrintMessage(L"\n FAILURE : Part failed to import. UniqueID = %d, Storage Name = %ls, Reference Name = %ls \n", uniqueID, pStorageName, pReferenceName);

			}

			delete pReference;
			pReference = NULL;
		}
	}
	return result;
}

//-----------------------------------------------------------------------------------------

int ImportPartPMI(SPAIopPartDocument& iIopPartDocument, const unsigned int& iRefId, const SPAIopPolicy& iPolicy)
{
	// Import the PMI data of a part using the Import method of a 
	// SPAIopAcisPartImporter object.  Use try/catch exception handling
	// blocks to manage fatal errors.
	int result = SPAIopSampleResult_Import_Ok;
	PrintMessage(L"\n ======= Processing Part PMI Data ======= \n");

	ENTITY_LIST* pAcisEntities = ACIS_NEW ENTITY_LIST();
	SPAIopAcisPartImporter partImporter;
	try
	{
		SPAIopPartImportResult partImportResult = partImporter.ImportWithPolicy(iPolicy, iIopPartDocument, *pAcisEntities);

		SPAIopPMIData pmiData = partImportResult.GetPMIData();
		SPAIopMappingData mappingData = partImportResult.GetMappingData();
		SPAIopPMIUtils::ProcessPartEntityCBFct = ProcessAcisPartEntity;
		SPAIopPMIUtils::AccessPMIData(pmiData, mappingData);
		// Store Part level Mapping data which is required while processing assembly level PMI.
		(SPAIopPMIUtils::_inputPSRefMappingDataMap)[iRefId] = new SPAIopMappingData(mappingData);
	}
	catch (SPAIopAcisError& /*error*/)
	{
		// Do not display the error because a missing part is not a fatal error
		//DisplayError( error );
		result = SPAIopSampleResult_Import_Part_Fail;
	}
	catch (...)
	{
		result = SPAIopSampleResult_Import_Part_Fail;
	}

	// Don't delete acis entities here. We need all part entities in case of assembly PMI.
	// Collect all acis entites here and then delete all these part entites at the end
	// after processing assembly level PMI.
	if (NULL != pAcisEntities && 0 < pAcisEntities->count())
	{
		toBeReleased.push_back(pAcisEntities);
	}
	return result;
}

//-----------------------------------------------------------------------------------------

void ImportAssemblyLevelPMI(SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy)
{
	PrintMessage(L"\n ======= Processing Assembly PMI Data ======= \n");

	SPAIopProductStructureDataImporter psDataImporter;
	try
	{
		SPAIopPSDataImportResult psDataImportRes = psDataImporter.ImportWithPolicy(iPolicy, iInputPS);

		SPAIopPMIData pmiData = psDataImportRes.GetPMIData();
		SPAIopMappingData mappingData;

		// Setting the CB to process entity links before accessing PMI data
		SPAIopPMIUtils::ProcessPartEntityCBFct = ProcessAcisPartEntity;
		bool isAssemblyPMI = true;
		SPAIopPMIUtils::AccessPMIData(pmiData, mappingData, isAssemblyPMI);
	}
	catch (SPAIopAcisError& error)
	{
		DisplayError(error);
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
	}
}

//-----------------------------------------------------------------------------------------

void ReleaseAcisEntities()
{
	// When **completely** done with these entities, release them.
	// This is especially useful when sequentially importing and processing many parts in one session.
	for (auto&& elist : toBeReleased) // nice, if your compiler supports it
	{
		SPAIopAcisSystem::ReleasePartEntities(*elist);
		ACIS_DELETE elist;
	}
	//for (std::vector<ENTITY_LIST*>::iterator iter = toBeReleased.begin(); iter != toBeReleased.end(); ++iter)
	//{
	//	SPAIopAcisSystem::ReleasePartEntities(**iter);
	//	ACIS_DELETE *iter;
	//}
	toBeReleased.clear();
}

//-----------------------------------------------------------------------------------------

int test_IopAcisPMI(int argc, char* argv[])
{
	int result(1);
	SPAIopPerformanceStatistics printPerfoStatsObj;

	// Work-flow similar to Acis based app where Acis will be generally initialized before IOP is called.
	api_start_modeller(0);

	try
	{
		// IOP is called in its own scope.
		// This sample uses a guard class that initializes InterOp upon 
		// construction and terminates InterOp upon destruction.  All 
		// translations are done within the boundaries of a single 
		// initialization and shutdown.  InterOp may only be initialized 
		// and shutdown once per process.  Please do not use the guard 
		// classes multiple times per process.
		SPAIopAcisSystemInitGuard initGuard(SPATIAL_LICENSE);

		result = initGuard.GetInitStatus();

		if (SPA_UNLOCK_NO_MSG == result)
		{
			// Spatial products have been unlocked
			std::wcout << L"SUCCESS: SPAIopAcisSystem::Initialize. " << std::endl;
		}
		else if (SPA_UNLOCK_KEY_WARN_EXPIRE == result)
			std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_KEY_WARN_EXPIRE; your license key will expire soon!\n";
		else if (SPA_UNLOCK_REGISTRATION_WARN_EXPIRE == result)
			std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_REGISTRATION_WARN_EXPIRE; your registration will expire soon.\n";
		else
		{
			std::wcout << L"SPAIopAcisSystem::Initialize returned an unexpected value; Contact Spatial Customer Support or your Account Manager.\n";
			return 1;
		}
		result = 1;


		char* argFields[] = { "i", "o", "p", "h", "log" };
		int size = sizeof(argFields) / sizeof(*argFields);
		SPAIopArgParser parser(size, argFields, argc, argv, &PrintHelp);

		if (true == parser.IsSuccess())
		{
			const wchar_t* pSourceFilePath = NULL;
			bool hasInputFileName = parser.GetWStringValueFor("i", pSourceFilePath);

			const wchar_t* pOutputFilePath = NULL;
			bool hasOutputFileName = parser.GetWStringValueFor("o", pOutputFilePath);

			const wchar_t* pPolicyFilePath = NULL;
			bool hasPolicyFileName = parser.GetWStringValueFor("p", pPolicyFilePath);

			const wchar_t* pLogFilePath = NULL;
			bool hasLogFileName = parser.GetWStringValueFor("log", pLogFilePath);

			if (hasInputFileName)
			{
				if (hasOutputFileName)
				{
					if (false == OpenFileForConsoleOutput(pOutputFilePath))
						return 11;
				}

				SPAIopPolicy policy;
				if (NULL != pPolicyFilePath)
				{
					SPAIopWString policyFilePath(pPolicyFilePath);
					SPAIopPolicyFileStreamXML xmlPolicyStream;
					xmlPolicyStream.SetUnstreamFilePath(policyFilePath);

					policy.Deserialize(xmlPolicyStream);
				}

				SetOrUpdateLogFileInPolicy(pLogFilePath, policy);

				result = ImportPMIData(pSourceFilePath, policy);
				CloseFileForConsoleOutput();
			}
			else
				PrintHelp();
		}
	}
	catch (SPAIopAcisError& fatalErr)
	{
		DisplayError(fatalErr);
		result = 1;
	}
	catch (...)
	{
		PrintMessage(L"FAILURE: Unknown exception caught! ");
		result = 1;
	}

	api_stop_modeller();

	return result;
}

//-----------------------------------------------------------------------------------------

